package cz.vutbr.fit.xzemko01.tfmcbs.validation.validators;

import java.util.Map;

import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.DiagnosticChain;

import cz.vutbr.fit.xzemko01.tfmcbs.model.Component;
import cz.vutbr.fit.xzemko01.tfmcbs.model.Interface;
import cz.vutbr.fit.xzemko01.tfmcbs.model.Provider;
import cz.vutbr.fit.xzemko01.tfmcbs.model.ProvidingPort;
import cz.vutbr.fit.xzemko01.tfmcbs.model.Realization;
import cz.vutbr.fit.xzemko01.tfmcbs.model.Relationship;
import cz.vutbr.fit.xzemko01.tfmcbs.model.SimpleComponent;
import cz.vutbr.fit.xzemko01.tfmcbs.validation.utils.DiagnosticBuilder;

public class RealizationValidator implements Validator<Realization> {
	private static RealizationValidator realizationValidator;
	
	private Validator<Relationship> relationshipValidator;
	
	private RealizationValidator() {
		relationshipValidator = RelationshipValidator.getInstance();
	}
	
	public static Validator<Realization> getInstance() {
		if(realizationValidator == null) {
			realizationValidator = new RealizationValidator();
		}
		
		return realizationValidator;
	}
	
	@Override
	public boolean validate(Realization realization, DiagnosticChain diagnostics, Map<Object, Object> context) {
		boolean result = relationshipValidator.validate(realization, diagnostics, context);
		result &= validateRealizationHasSetInterface(realization, diagnostics, context);
		result &= validateRealizationHasSetProvider(realization, diagnostics, context);
		result &= validateRealizationHasProviderAndInterfaceInSameContainer(realization, diagnostics, context);
		return result;
	}
	
	private boolean validateRealizationHasSetInterface(Realization realization, DiagnosticChain diagnostics, Map<Object, Object> context) {
		if(realization.getInterface() == null) {
			Diagnostic diagnostic = new DiagnosticBuilder(realization)
				.setMessage("Realization with name \"" + realization.getName() + "\" has not set interface.").build();
			
			diagnostics.add(diagnostic);
			return false;
		}
		return true;
	}
	
	private boolean validateRealizationHasSetProvider(Realization realization, DiagnosticChain diagnostics, Map<Object, Object> context) {
		if(realization.getProvider() == null) {
			Diagnostic diagnostic = new DiagnosticBuilder(realization)
				.setMessage("Realization with name \"" + realization.getName() + "\" has not set provider.").build();
			
			diagnostics.add(diagnostic);
			return false;
		}
		return true;
	}
	
	private boolean validateRealizationHasProviderAndInterfaceInSameContainer(Realization realization, DiagnosticChain diagnostics, Map<Object, Object> context) {
		Provider provider = realization.getProvider();
		Interface _interface = realization.getInterface();
		if(provider == null || _interface == null) {
			return true;
		}
		
		boolean result = true;
		if(provider instanceof ProvidingPort) {
			
			Component component = ((ProvidingPort) provider).getComponent(); 
			if(component != null && component.getContainer() != _interface.getContainer()) {
				String errorMessage = 
						"Interface \"" + _interface.getName() + "\"" +
						" and port \"" + ((ProvidingPort) provider).getName() + "\" " +
						"in the component \"" + component.getName() + "\" must be in same container.";
				
				Diagnostic diagnostic = new DiagnosticBuilder(realization).setMessage(errorMessage).build();
				diagnostics.add(diagnostic);
				result = false;
			}
		} else if(provider instanceof SimpleComponent) {
			SimpleComponent component = (SimpleComponent) provider;
			if(component.getContainer() != _interface.getContainer()) {
				String errorMessage = 
						"Interface \"" + _interface.getName() + "\"" +
						" and component \"" + component.getName() + "\" must be in same container.";
				
				Diagnostic diagnostic = new DiagnosticBuilder(realization).setMessage(errorMessage).build();
				diagnostics.add(diagnostic);
				result = false;
			}
		}
		
		return result;
	}
}